热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

更多|上文_SpringBoot项目的两种打包方式分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了SpringBoot项目的两种打包方式分析相关的知识,希望对你有一定的参考价值。点击上方关注“终端研发

篇首语:本文由编程笔记#小编为大家整理,主要介绍了SpringBoot项目的两种打包方式分析相关的知识,希望对你有一定的参考价值。


点击上方关注 “终端研发部



设为“星标”,和你一起掌握更多数据库知识

作者: 枕边书
来源: zhenbianshu.github.io

Part1前言

最近对 Spring 越来越感兴趣,却在阅读它的源码时很容易被类之间的跳转和方法的嵌套绕晕,为了避免无尽的烦恼,我决定跟它做一个了断,不再追求细节,了解其启动过程和重要组件即可,之后遇到细节问题再看对应模块的源码。


我们都知道,一个 Java Web 服务进程,Web 服务器是其必不可少的组件之一,仅有 Spring 是无法受理系统的 HTTP 请求的。而且在 Java 的 Servlet 模型里,Spring 是作为 Web 服务器里的一个 Servlet 存在的,这就更能说明 Spring 和 Web 服务器的关系之亲密了。


但每个 Java 进程都只有一个主类,进程从其静态的 main 函数里启动,那么在 Spring 和 Web 服务器配合的场景中,Spring 和 Web 服务器之间是谁先启动的呢?Spring 容器和 Servlet 容器之间有包含关系吗?


要回答这两个问题,还需要分场景来看。


由于 Java Web 服务器里我们最熟悉 Tomcat,后面就以 Tomcat 来代表 Web 服务器了,而 Spring 相关我们都基于 SpringBoot 来说。


Part2Jar 包方式

实例


我们最常看到的是下面例子中的写法。


@SpringBootApplication
public class ServerStarter 
    public static void main(String[] args) 
        SpringApplication.run(ServerStarter.class, args);
    

这种方式非常符合我们对 Java 进程启动的认知。可以看到,我们在 ServerStarter.main 方法里直接使用了 SpringApplication.run() 方法,而 Spring 启动的流程都在 SpringApplication 类内,我们后续再详细分析。


在 IDE 里我们可以直接执行这个主类来启动 Spring 应用,而脱离了 IDE,我们就只能把整个应用打成一个 fat jar,并且在 jar 包的 MANIFEST 文件内设置 Main-Class 属性值为 package.path.ServerStarter 来声明主类。这样,在使用 java -jar spring_starter.jar 时, JVM 就知道该从哪个类开始加载。


联系


Spring 容器是启动成功后, Tomcat Web 服务器的启动就要靠 Spring 了,我查看了 SpringApplication 类的代码整理出以下流程。


  1. 创建 SpringApplication 时使用在 deduceWebApplicationType() 通过一些标志类(如 org.springframework.web.servlet.DispatcherServlet) 是否存在,推断是出应用类型,一般是 WebApplicationType.SERVLET

  2. 创建 createApplicationContext() 方法内,根据应用类型创建出一个 AnnotationConfigServletWebServerApplicationContext 类型的 ConfigurableApplicationContext。

  3. 由于 AnnotationConfigServletWebServerApplicationContext 类是 ServletWebServerApplicationContext 的子类,在 ApplicationContext.refresh() 时,会通过 ServletWebServerApplicationContext.onRefresh() 方法创建一个 webServer。

  4. DispatcherServlet 通过 ServletContextInitializer -> ServletRegistrationBean 注册到 Tomcat 容器内,完成两者的关联。


Part3War 包方式

实例


在生产环境部署服务时,我们更多地使用 war 包的方式,因为它可以很方便地支持 jsp,而通过 jsp 我们可以给生产环境调试添加一定的灵活性。在使用 docker 部署时,也能将 Tomcat 和服务分层,便于镜像的维护。


我们知道,一个 war 包是没有自运行能力的,必须要先启动一个 Tomcat 进程,由 Tomcat 自动解压并加载 webapps 目录下的 war 包来启动服务,所以通过 war 包启动的 Spring Web 主类一定是 Tomcat 类。


在主类已存在的情况下,我们的 Spring 入口类就不再需要 main 方法了,常见写法如下:


@SpringBootApplication
public class ServerStarter extends SpringBootServletInitializer 
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
        return application.sources(ServerStarter.class);
    

可以看到,Spring 入口类继承了 SpringBootServletInitializer 类,最上层的接口是 org.springframework.web;.WebApplicationInitializer


联系


查看 Tomcat 的 web.xml 配置,没有一丝 spring 的痕迹,要知道 Tomcat 是如何联系上 Spring 的,还要从 WebApplicationInitializer 接口开始。


这种实现方式需要两种新特性的支持:


  • Java 1.6 之后新添加了一个类 java.util.ServiceLoader,提供了一种新的有别于 ClassLoader 的类发现机制。当我们在 META-INF/services 文件夹内添加文件,文件名是全路径的接口,文件内容每一行是全路径的接口实现,就可以通过 ServiceLoader.load(Class interface) 方法加载到对应的接口实现,这种机制就是 SPI (Service Provider Interface),使用这种机制,加载用户实现的特定接口时就不需要类加载器扫描所有的类文件了。不过如果要加载的接口是 Java 底层类时,类加载器会是 BootstrapClassLoader 或 ExtClassLoader,加载子类时需要指定使用 classLoader 为 currentThreadClassLoader (一般是加载应用的 AppClassLoader)。

  • Servlet 3.0+ 提供了另一种替代 web.xml 的 Servlet 配置机制 ServletContainerInitializer 接口,Tomcat 会在 Servlet 容器创建后调用 ServletContainerInitializer.onStartup() 方法,便于我们向容器内注册一些 Servlet 对象。


我一路跟随文档向上查看,总结了一下整个流程。


  1. Spring 在 web 包的 META-INF/services 内添加了文件 javax.servlet.ServletContainerInitializer,内容为 org.springframework.web.SpringServletContainerInitializer。

  2. Tomcat 容器启动后,通过 ServiceLoader 加载到 SpringServletContainerInitializer 实例。

  3. SpringServletContainerInitializer 通过 @HandlesTypes 注解获取到 WebApplicationInitializer 接口的所有实现(包括 SpringBootServletInitializer、AbstractDispatcherServletInitializer 等)。

  4. SpringBootServletInitializer.onStartup() 方法内初始化了 Spring 容器。

  5. AbstractDispatcherServletInitializer.registerDispatcherServlet() 方法内实现了 DispatcherServlet 与 Tomcat 的关联。


Part4小结

看完了两种服务打包方式下 Spring 容器被加载的过程,文章开头的两个问题应该就有迹可循了。


首先 Spring 和 Web 服务器的启动先后会根据服务打包方式有所不同,使用 jar 包时是 Spring 先启动,而使用 war 包时是 Web 服务器先启动。


而 Spring 容器与 Servlet 容器的包含关系,我理解是并不存在的,A 启动了 B,所以 A 包含 B 的理论由上文也可以看出是站不住脚的。Servlet 容器和 Spring 之间只是存储的元素不同,Servlet 容器内存放着 Servlet 实例,而 Spring 中存放着各种环境变量、Bean 对象等。而它们之前的联系就是 DispatcherServlet,它既是一个 Servlet 实例,又是一个 Bean,通过 DispatcherServlet,Tomcat 可以调用到 Spring 容器内部的对象,从线程栈上来看是 Web 服务器在下,Spring 更往上。




BAT等大厂Java面试经验总结




想获取 Java大厂面试题学习资料


扫下方二维码回复「BAT」就好了


回复 【加群】获取github掘金交流群
回复 【电子书】获取2020电子书教程
回复 【C】获取全套C语言学习知识手册
回复 【Java】获取java相关的视频教程和资料
回复 【爬虫】获取SpringCloud相关多的学习资料
回复 【Python】即可获得Python基础到进阶的学习教程
回复 【idea破解】即可获得intellij idea相关的破解教程
关注我gitHub掘金,每天发掘一篇好项目,学习技术不迷路!

如果喜欢就给个“在看”

推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • Jboss的EJB部署描述符standardjaws.xml配置步骤详解
    本文详细介绍了Jboss的EJB部署描述符standardjaws.xml的配置步骤,包括映射CMP实体EJB、数据源连接池的获取以及数据库配置等内容。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
author-avatar
DilWilling
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有